/**
 * 文件处理工作器
 * EndoSight-UC 医疗AI系统
 *
 * 异步处理医学文件上传、验证和预处理
 * 支持流式处理和大文件优化
 */

import queueManager from '../config/queueConfig.js';
import structuredLogger from '../utils/structuredLogger.js';
import auditService from '../services/auditService.js';
import { QueueType, QueuePriority } from '../config/queueConfig.js';
import fs from 'fs';
import path from 'path';
import crypto from 'crypto';
import winston from 'winston';
import { Worker } from 'worker_threads';
import os from 'os';

// 配置日志记录器
const logger = winston.createLogger({
  level: 'info',
  format: winston.format.combine(
    winston.format.timestamp(),
    winston.format.json()
  ),
  transports: [
    new winston.transports.Console({
      format: winston.format.simple()
    }),
    new winston.transports.File({
      filename: 'logs/file-processing-worker.log',
      level: 'info'
    })
  ]
});

/**
 * 文件处理工作器主类
 */
class FileProcessingWorker {
  constructor() {
    this.isRunning = false;
    this.workers = [];
    this.maxWorkers = Math.min(os.cpus().length, 2); // 文件处理使用较少工作进程
    this.processedFiles = 0;
    this.failedFiles = 0;
    this.totalBytesProcessed = 0;
    this.startTime = null;
    this.queue = null;
  }

  /**
   * 启动文件处理工作器
   */
  async start() {
    try {
      logger.info('正在启动文件处理工作器...', {
        maxWorkers: this.maxWorkers,
        nodeVersion: process.version,
        platform: os.platform()
      });

      // 获取文件处理队列
      this.queue = queueManager.getQueue(QueueType.FILE_PROCESSING);
      if (!this.queue) {
        logger.warn('文件处理队列未初始化，检查Redis连接状态');
        if (!queueManager.redisAvailable) {
          logger.warn('Redis不可用，文件处理工作器将以降级模式运行');
          this.isRunning = true;
          this.startTime = Date.now();
          return;
        }
        throw new Error('文件处理队列未初始化');
      }

      // 设置队列处理器
      this.setupQueueProcessor();

      this.isRunning = true;
      this.startTime = Date.now();

      logger.info('✅ 文件处理工作器启动成功', {
        workerCount: this.maxWorkers,
        pid: process.pid
      });

      // 设置优雅关闭
      this.setupGracefulShutdown();

      return true;
    } catch (error) {
      logger.error('文件处理工作器启动失败:', error);
      throw error;
    }
  }

  /**
   * 设置队列处理器
   */
  setupQueueProcessor() {
    // 医学图像处理
    this.queue.process('medical-image-upload', this.maxWorkers, async (job) => {
      return await this.processMedicalImageUpload(job);
    });

    // 大文件流式处理
    this.queue.process('large-file-streaming', 2, async (job) => {
      return await this.processLargeFileStreaming(job);
    });

    // 文件验证和清理
    this.queue.process('file-validation', 3, async (job) => {
      return await this.processFileValidation(job);
    });

    // 文件格式转换
    this.queue.process('format-conversion', 1, async (job) => {
      return await this.processFormatConversion(job);
    });

    // 队列事件监听
    this.queue.on('completed', (job, result) => {
      this.processedFiles++;
      this.totalBytesProcessed += result.fileSize || 0;
      this.logFileCompletion(job, result);
    });

    this.queue.on('failed', (job, error) => {
      this.failedFiles++;
      this.logFileFailure(job, error);
    });

    this.queue.on('stalled', (job) => {
      logger.warn('文件处理任务停滞', {
        jobId: job.id,
        fileName: job.data.fileName,
        timestamp: new Date().toISOString()
      });
    });
  }

  /**
   * 处理医学图像上传
   */
  async processMedicalImageUpload(job) {
    const startTime = Date.now();
    const { data } = job;

    try {
      logger.info('开始处理医学图像上传', {
        jobId: job.id,
        fileName: data.fileName,
        fileSize: data.fileSize,
        userId: data.userId,
        taskId: data.taskId
      });

      // 记录审计日志
      await auditService.logFileOperation(
        data.userId,
        data.sessionId,
        'upload',
        data.fileName,
        data.filePath,
        data.fileType,
        data.fileSize,
        data.fileHash,
        data.mimeType,
        true, // medical_image
        data.patientId,
        data.taskId,
        null, // upload_result待更新
        null, // validation_result待更新
        null, // storage_location
        true, // access_granted
        null, // access_denied_reason
        true, // retention_required
        null, // retention_until
        false, // gdpr_data
        {
          jobStarted: new Date().toISOString(),
          processingType: 'medical-image-upload',
          priority: data.priority || QueuePriority.NORMAL
        }
      );

      // 验证文件完整性
      const validationResult = await this.validateMedicalImage(data);

      if (!validationResult.isValid) {
        throw new Error(`文件验证失败: ${validationResult.errors.join(', ')}`);
      }

      // 生成文件哈希
      const fileHash = await this.generateFileHash(data.filePath);

      // 检测PHI数据
      const phiDetected = await this.detectPHIInFile(data.filePath, data.fileType);

      // 更新文件元数据
      const metadata = await this.updateFileMetadata(data, {
        fileHash,
        phiDetected,
        validationResult,
        processingStatus: 'completed'
      });

      const duration = Date.now() - startTime;

      // 更新审计日志
      await this.updateFileAuditLog(data, {
        uploadResult: { success: true, duration },
        validationResult,
        fileHash,
        phiDetected
      }, duration, 'success');

      logger.info('医学图像上传处理完成', {
        jobId: job.id,
        fileName: data.fileName,
        duration,
        fileHash,
        phiDetected: phiDetected.length
      });

      return {
        success: true,
        fileName: data.fileName,
        filePath: data.filePath,
        fileHash,
        fileSize: data.fileSize,
        phiDetected,
        validationResult,
        duration,
        timestamp: new Date().toISOString()
      };

    } catch (error) {
      const duration = Date.now() - startTime;

      logger.error('医学图像上传处理失败', {
        jobId: job.id,
        fileName: data.fileName,
        error: error.message,
        duration,
        attempts: job.attemptsMade
      });

      // 更新审计日志
      await this.updateFileAuditLog(data, {
        uploadResult: { success: false, error: error.message }
      }, duration, 'failed', error.message);

      // 清理失败的文件
      await this.cleanupFailedFile(data);

      throw error;
    }
  }

  /**
   * 处理大文件流式处理
   */
  async processLargeFileStreaming(job) {
    const { data } = job;
    const startTime = Date.now();

    try {
      logger.info('开始大文件流式处理', {
        jobId: job.id,
        fileName: data.fileName,
        fileSize: data.fileSize,
        chunks: data.chunkCount
      });

      const processedChunks = [];
      const totalSize = data.fileSize;
      let processedSize = 0;

      // 处理文件块
      for (let i = 0; i < data.chunkCount; i++) {
        const chunkPath = `${data.filePath}.chunk.${i}`;

        if (fs.existsSync(chunkPath)) {
          const chunkStats = fs.statSync(chunkPath);
          processedSize += chunkStats.size;

          processedChunks.push({
            chunkIndex: i,
            size: chunkStats.size,
            path: chunkPath,
            hash: await this.generateFileHash(chunkPath)
          });

          // 更新进度
          const progress = (processedSize / totalSize) * 100;
          await job.progress(progress);

          logger.debug(`文件块 ${i} 处理完成`, {
            progress: progress.toFixed(2) + '%',
            processedSize,
            totalSize
          });
        }
      }

      // 合并文件块
      await this.mergeFileChunks(data.filePath, processedChunks);

      // 验证合并后的文件
      const finalHash = await this.generateFileHash(data.filePath);
      const expectedHash = data.expectedHash;

      if (finalHash !== expectedHash) {
        throw new Error('文件完整性验证失败：哈希值不匹配');
      }

      const duration = Date.now() - startTime;

      logger.info('大文件流式处理完成', {
        jobId: job.id,
        fileName: data.fileName,
        duration,
        finalSize: processedSize,
        chunkCount: processedChunks.length
      });

      return {
        success: true,
        fileName: data.fileName,
        filePath: data.filePath,
        fileSize: processedSize,
        chunkCount: processedChunks.length,
        finalHash,
        duration,
        timestamp: new Date().toISOString()
      };

    } catch (error) {
      logger.error('大文件流式处理失败', {
        jobId: job.id,
        fileName: data.fileName,
        error: error.message
      });

      // 清理文件块
      await this.cleanupFileChunks(data.filePath, data.chunkCount);

      throw error;
    }
  }

  /**
   * 处理文件验证
   */
  async processFileValidation(job) {
    const { data } = job;

    try {
      logger.debug('开始文件验证', {
        jobId: job.id,
        fileName: data.fileName,
        validationType: data.validationType
      });

      const validationResults = {
        isValid: true,
        errors: [],
        warnings: [],
        metadata: {}
      };

      // 基本文件验证
      if (!fs.existsSync(data.filePath)) {
        validationResults.isValid = false;
        validationResults.errors.push('文件不存在');
        return validationResults;
      }

      const stats = fs.statSync(data.filePath);
      validationResults.metadata.fileSize = stats.size;
      validationResults.metadata.lastModified = stats.mtime;
      validationResults.metadata.createdAt = stats.birthtime;

      // 医学图像特定验证
      if (data.isMedicalImage) {
        await this.validateMedicalImageFormat(data, validationResults);
      }

      // 安全性验证
      await this.validateFileSecurity(data, validationResults);

      // PHI检测
      const phiDetected = await this.detectPHIInFile(data.filePath, data.fileType);
      validationResults.metadata.phiDetected = phiDetected;

      if (phiDetected.length > 0) {
        validationResults.warnings.push(`检测到${phiDetected.length}个潜在PHI字段`);
      }

      logger.debug('文件验证完成', {
        jobId: job.id,
        fileName: data.fileName,
        isValid: validationResults.isValid,
        errorCount: validationResults.errors.length,
        warningCount: validationResults.warnings.length
      });

      return validationResults;

    } catch (error) {
      logger.error('文件验证失败', {
        jobId: job.id,
        fileName: data.fileName,
        error: error.message
      });

      throw error;
    }
  }

  /**
   * 处理格式转换
   */
  async processFormatConversion(job) {
    const { data } = job;

    try {
      logger.info('开始文件格式转换', {
        jobId: job.id,
        fileName: data.fileName,
        sourceFormat: data.sourceFormat,
        targetFormat: data.targetFormat
      });

      // 这里可以添加实际的格式转换逻辑
      // 例如：医学图像格式转换（DICOM到PNG等）

      const duration = Date.now() - Date.now();

      logger.info('文件格式转换完成', {
        jobId: job.id,
        fileName: data.fileName,
        duration
      });

      return {
        success: true,
        originalFile: data.fileName,
        convertedFile: data.fileName.replace(/\.[^/.]+$/, `.${data.targetFormat}`),
        duration,
        timestamp: new Date().toISOString()
      };

    } catch (error) {
      logger.error('文件格式转换失败', {
        jobId: job.id,
        fileName: data.fileName,
        error: error.message
      });

      throw error;
    }
  }

  /**
   * 验证医学图像
   */
  async validateMedicalImage(fileData) {
    const result = {
      isValid: true,
      errors: [],
      warnings: []
    };

    try {
      // 检查文件大小
      if (fileData.fileSize > 50 * 1024 * 1024) { // 50MB限制
        result.warnings.push('文件大小超过50MB，可能影响处理性能');
      }

      // 检查文件扩展名
      const allowedExtensions = ['.jpg', '.jpeg', '.png', '.bmp', '.tiff', '.dicom', '.dcm'];
      const fileExtension = path.extname(fileData.fileName).toLowerCase();

      if (!allowedExtensions.includes(fileExtension)) {
        result.errors.push(`不支持的文件格式: ${fileExtension}`);
        result.isValid = false;
      }

      // 检查MIME类型
      const allowedMimeTypes = [
        'image/jpeg', 'image/png', 'image/bmp', 'image/tiff',
        'application/dicom'
      ];

      if (fileData.mimeType && !allowedMimeTypes.includes(fileData.mimeType)) {
        result.errors.push(`不支持的MIME类型: ${fileData.mimeType}`);
        result.isValid = false;
      }

      // 检查文件内容
      if (fs.existsSync(fileData.filePath)) {
        const buffer = fs.readFileSync(fileData.filePath, { encoding: null });

        // 简单的图像文件头检查
        const signatures = {
          'image/jpeg': [0xFF, 0xD8, 0xFF],
          'image/png': [0x89, 0x50, 0x4E, 0x47],
          'image/bmp': [0x42, 0x4D],
          'image/tiff': [0x49, 0x49, 0x2A, 0x00]
        };

        let isValidImage = false;
        for (const [mimeType, signature] of Object.entries(signatures)) {
          if (buffer.length >= signature.length &&
              signature.every((byte, index) => buffer[index] === byte)) {
            isValidImage = true;
            break;
          }
        }

        if (!isValidImage) {
          result.errors.push('文件内容与图像格式不匹配');
          result.isValid = false;
        }
      }

      return result;

    } catch (error) {
      result.errors.push(`验证过程中发生错误: ${error.message}`);
      result.isValid = false;
      return result;
    }
  }

  /**
   * 验证医学图像格式
   */
  async validateMedicalImageFormat(fileData, validationResults) {
    // DICOM文件特殊验证
    if (fileData.fileType === 'application/dicom' || fileData.fileName.endsWith('.dcm')) {
      // 这里可以添加DICOM文件特定的验证逻辑
      validationResults.metadata.isDICOM = true;
    }

    // 检查图像分辨率
    // 这里可以添加图像分辨率检查逻辑
  }

  /**
   * 验证文件安全性
   */
  async validateFileSecurity(fileData, validationResults) {
    try {
      // 检查文件路径是否安全
      const uploadPath = path.normalize(fileData.filePath);
      if (uploadPath.includes('..') || uploadPath.includes('//')) {
        validationResults.errors.push('文件路径不安全');
        validationResults.isValid = false;
      }

      // 检查文件权限
      if (fs.existsSync(fileData.filePath)) {
        const stats = fs.statSync(fileData.filePath);
        // 检查文件是否可读
        try {
          fs.accessSync(fileData.filePath, fs.constants.R_OK);
        } catch (error) {
          validationResults.errors.push('文件无法读取');
          validationResults.isValid = false;
        }
      }

      // 检查文件名是否包含恶意字符
      const maliciousPatterns = [/<script/i, /javascript:/i, /vbscript:/i];
      for (const pattern of maliciousPatterns) {
        if (pattern.test(fileData.fileName)) {
          validationResults.errors.push('文件名包含潜在恶意内容');
          validationResults.isValid = false;
          break;
        }
      }

    } catch (error) {
      validationResults.errors.push(`安全验证失败: ${error.message}`);
      validationResults.isValid = false;
    }
  }

  /**
   * 生成文件哈希
   */
  async generateFileHash(filePath) {
    try {
      const hash = crypto.createHash('sha256');
      const stream = fs.createReadStream(filePath);

      return new Promise((resolve, reject) => {
        stream.on('data', (data) => hash.update(data));
        stream.on('end', () => resolve(hash.digest('hex')));
        stream.on('error', reject);
      });

    } catch (error) {
      logger.error('生成文件哈希失败:', error);
      return null;
    }
  }

  /**
   * 检测文件中的PHI数据
   */
  async detectPHIInFile(filePath, fileType) {
    const phiFields = [];

    try {
      // 对于图像文件，这里可以添加OCR和PHI检测逻辑
      if (fileType.startsWith('image/')) {
        // 这里可以调用OCR服务检测图像中的文本
        // 然后对文本进行PHI检测
      }

      // 对于DICOM文件，可以解析元数据
      if (fileType === 'application/dicom') {
        // 这里可以解析DICOM元数据并检测PHI
      }

    } catch (error) {
      logger.error('PHI检测失败:', error);
    }

    return phiFields;
  }

  /**
   * 更新文件元数据
   */
  async updateFileMetadata(fileData, metadata) {
    try {
      // 这里可以更新数据库中的文件元数据
      logger.debug('文件元数据已更新', {
        fileName: fileData.fileName,
        metadata
      });

      return metadata;

    } catch (error) {
      logger.error('更新文件元数据失败:', error);
      return null;
    }
  }

  /**
   * 更新文件审计日志
   */
  async updateFileAuditLog(fileData, additionalData, duration, status, errorMessage = null) {
    try {
      await auditService.logFileOperation(
        fileData.userId,
        fileData.sessionId,
        'upload',
        fileData.fileName,
        fileData.filePath,
        fileData.fileType,
        fileData.fileSize,
        additionalData.fileHash || fileData.fileHash,
        fileData.mimeType,
        true, // medical_image
        fileData.patientId,
        fileData.taskId,
        additionalData.uploadResult,
        additionalData.validationResult,
        fileData.filePath, // storage_location
        status === 'success', // access_granted
        status === 'success' ? null : errorMessage, // access_denied_reason
        true, // retention_required
        null, // retention_until
        false, // gdpr_data
        {
          ...additionalData,
          processingDuration: duration,
          finalStatus: status,
          completedAt: new Date().toISOString()
        }
      );

    } catch (error) {
      logger.error('更新文件审计日志失败:', error);
    }
  }

  /**
   * 合并文件块
   */
  async mergeFileChunks(filePath, chunks) {
    try {
      const writeStream = fs.createWriteStream(filePath);

      for (const chunk of chunks) {
        const chunkStream = fs.createReadStream(chunk.path);

        await new Promise((resolve, reject) => {
          chunkStream.pipe(writeStream, { end: false });
          chunkStream.on('end', resolve);
          chunkStream.on('error', reject);
        });
      }

      writeStream.end();

      // 删除文件块
      for (const chunk of chunks) {
        fs.unlinkSync(chunk.path);
      }

    } catch (error) {
      logger.error('合并文件块失败:', error);
      throw error;
    }
  }

  /**
   * 清理失败的文件
   */
  async cleanupFailedFile(fileData) {
    try {
      if (fs.existsSync(fileData.filePath)) {
        fs.unlinkSync(fileData.filePath);
        logger.info('已清理失败的文件', { filePath: fileData.filePath });
      }
    } catch (error) {
      logger.error('清理失败文件时出错:', error);
    }
  }

  /**
   * 清理文件块
   */
  async cleanupFileChunks(filePath, chunkCount) {
    try {
      for (let i = 0; i < chunkCount; i++) {
        const chunkPath = `${filePath}.chunk.${i}`;
        if (fs.existsSync(chunkPath)) {
          fs.unlinkSync(chunkPath);
        }
      }
    } catch (error) {
      logger.error('清理文件块时出错:', error);
    }
  }

  /**
   * 记录文件完成日志
   */
  logFileCompletion(job, result) {
    logger.info('文件处理任务完成', {
      jobId: job.id,
      fileName: job.data.fileName,
      fileSize: result.fileSize,
      duration: result.duration,
      processedFiles: this.processedFiles,
      failedFiles: this.failedFiles
    });
  }

  /**
   * 记录文件失败日志
   */
  logFileFailure(job, error) {
    logger.error('文件处理任务失败', {
      jobId: job.id,
      fileName: job.data.fileName,
      error: error.message,
      attempts: job.attemptsMade,
      processedFiles: this.processedFiles,
      failedFiles: this.failedFiles
    });
  }

  /**
   * 设置优雅关闭
   */
  setupGracefulShutdown() {
    const shutdown = async (signal) => {
      logger.info(`收到${signal}信号，正在优雅关闭文件处理工作器...`);

      this.isRunning = false;

      try {
        if (this.queue) {
          await this.queue.pause();
        }

        logger.info('等待当前文件处理任务完成...');
        await new Promise(resolve => setTimeout(resolve, 10000)); // 10秒超时

        logger.info('✅ 文件处理工作器已优雅关闭', {
          processedFiles: this.processedFiles,
          failedFiles: this.failedFiles,
          totalBytesProcessed: this.totalBytesProcessed,
          uptime: this.startTime ? Date.now() - this.startTime : 0
        });

        process.exit(0);

      } catch (error) {
        logger.error('优雅关闭过程中发生错误:', error);
        process.exit(1);
      }
    };

    process.on('SIGTERM', () => shutdown('SIGTERM'));
    process.on('SIGINT', () => shutdown('SIGINT'));
    process.on('SIGUSR2', () => shutdown('SIGUSR2'));
  }

  /**
   * 获取工作器状态
   */
  getStatus() {
    return {
      isRunning: this.isRunning,
      processedFiles: this.processedFiles,
      failedFiles: this.failedFiles,
      totalBytesProcessed: this.totalBytesProcessed,
      uptime: this.startTime ? Date.now() - this.startTime : 0,
      queueStatus: this.queue ? 'active' : 'inactive'
    };
  }
}

// 创建并导出工作器实例
const fileProcessingWorker = new FileProcessingWorker();

export default fileProcessingWorker;